//
//  TabManager.m
//  CLBrowser
//
//  Created by 梁慧聪 on 21/7/30.
//  Copyright © 2021年 梁慧聪. All rights reserved.
//

#import "CLWebViewManager.h"
#import "CLWebDataView.h"
#import "CLVCMain.h"
#import "CLErrorPageHelper.h"
#import "CLWebContainerView.h"
#import "CLSessionData.h"
#import "CLWebViewBackForwardList.h"
#import "CLHistorySQLiteManager.h"
#import "CLExtentionsManager.h"
#import "CLUserDefaults.h"
#import "CLURLConnectionDelegateProxy.h"
#import "UIAlertAction+CLUtility.h"
#import "CLWebViewBridge.h"
#import <CommonCrypto/CommonDigest.h>

typedef void(^JSBlock)(CLWebView *);

static NSString *const MULTI_WINDOW_FILE_NAME    = @"multiWindowHis.plist";
static NSString *const MY_HISTORY_DATA_KEY       = @"multiWindowHisData";
static NSString *const MULTI_WINDOW_IMAGE_FOLDER = @"multiWindowImages";

static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey;

#ifdef DEBUG
#define QueueCheck(shouldSyncQueue) do {                                                       \
CLWebViewManager *manager = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);                                            \
assert((shouldSyncQueue ? manager == self : manager != self) && "operate on webModelArray needs in sync queue");   \
} while (0)
#else
#define QueueCheck(shouldSyncQueue)
#endif

@implementation CLWebModel

- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        _title = [aDecoder decodeObjectOfClass:[NSString class] forKey:KEY_WEB_TITLE];
        _url = [aDecoder decodeObjectOfClass:[NSString class] forKey:KEY_WEB_URL];
        _imageKey = [aDecoder decodeObjectOfClass:[NSString class] forKey:KEY_WEB_IMAGE_URL];
        _sessionData = [aDecoder decodeObjectOfClass:[CLSessionData class] forKey:KEY_WEB_SESSION_DATA];
    }
    
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.title forKey:KEY_WEB_TITLE];
    [aCoder encodeObject:self.url forKey:KEY_WEB_URL];
    [aCoder encodeObject:self.imageKey forKey:KEY_WEB_IMAGE_URL];
    [aCoder encodeObject:self.sessionData forKey:KEY_WEB_SESSION_DATA];
}

+ (BOOL)supportsSecureCoding {
    return YES;
}
- (void)setImage:(UIImage *)image {
    _image = image;
}
@end

@interface CLWebViewManager () <CLWebViewDelegate>

@property (nonatomic, strong) NSMutableArray<CLWebModel *> *webModelArray;
@property (nonatomic, copy)   NSString *filePath;
@property (nonatomic, copy)   NSString *imagesFolderPath;
@property (nonatomic, strong) dispatch_queue_t synchQueue;

@end

@implementation CLWebViewManager

SYNTHESIZE_SINGLETON_FOR_CLASS(CLWebViewManager);

- (instancetype)init {
    if (self = [super init]) {
        _filePath = [DocumentPath stringByAppendingPathComponent:MULTI_WINDOW_FILE_NAME];
        _imagesFolderPath = [DocumentPath stringByAppendingPathComponent:MULTI_WINDOW_IMAGE_FOLDER];
        
        NSString *queueName = [NSString stringWithFormat:@"com.cl.TabManager-%@", [[NSUUID UUID] UUIDString]];
        _synchQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
        dispatch_queue_set_specific(_synchQueue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL);
        
        _webModelArray = [NSMutableArray arrayWithCapacity:4];
        [self loadWebModelArray];
        
        [self registerObserver];
        NSLog(@"保存目录：%@",_imagesFolderPath);
    }
    
    return self;
}

- (void)registerObserver {
    [Notifier addObserver:self selector:@selector(clearMemory) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    [Notifier addObserver:self selector:@selector(cleanDisk) name:UIApplicationWillTerminateNotification object:nil];
    [Notifier addObserver:self selector:@selector(backgroundCleanDisk) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [Notifier addObserver:self selector:@selector(noImageModeChanged) name:kNoImageModeChanged object:nil];
    [Notifier addObserver:self selector:@selector(eyeProtectiveModeChanged) name:kEyeProtectiveModeChanged object:nil];
    [Notifier addObserver:self selector:@selector(nightStatueModeChanged) name:kNightStatueModeChanged object:nil];
    [[CLDelegateManager sharedInstance] registerDelegate:self forKey:kDelegateManagerWebView];
}

- (BOOL)isCurrentWebView:(CLWebView *)webView {
    if (webView == self.clContainerView.webView) {
        return YES;
    }
    return NO;
}

- (NSArray<NSString *> *)getBackForwardListURL:(CLWebViewBackForwardList *)list {
    NSMutableArray *array = [NSMutableArray array];
    [list.backList enumerateObjectsUsingBlock:^(CLWebViewHistoryItem *item, NSUInteger idx, BOOL *stop){
        NSString *url = (item.URLString ? item.URLString : @"");
        [array addObject:url];
    }];
    
    if (list.currentItem) {
        NSString *url = (list.currentItem.URLString ? list.currentItem.URLString : @"");
        [array addObject:url];
    }
    
    [list.forwardList enumerateObjectsUsingBlock:^(CLWebViewHistoryItem *item, NSUInteger idx, BOOL *stop){
        NSString *url = (item.URLString ? item.URLString : @"");
        [array addObject:url];
    }];
    
    return [array copy];
}

- (void)loadWebModelArray {
    dispatch_async(_synchQueue, ^{
        if ([[NSFileManager defaultManager] fileExistsAtPath:self.filePath]) {
            NSData *data = [NSData dataWithContentsOfFile:self.filePath options:NSDataReadingUncached error:nil];
            if (data) {
                NSKeyedUnarchiver *unarchiver;
                @try {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
                    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
#pragma clang diagnostic pop
                    NSArray<CLWebModel *> *array = [unarchiver decodeObjectForKey:MY_HISTORY_DATA_KEY];
                    
                    if (array && [array isKindOfClass:[NSArray<CLWebModel *> class]] && array.count > 0) {
                        [self.webModelArray addObjectsFromArray:array];
                    }
                    else{
                        [self setDefaultWebArray];
                    }
                } @catch (NSException *exception) {
                    [self setDefaultWebArray];
                } @finally {
                    [unarchiver finishDecoding];
                }
                
            } else {
                [self setDefaultWebArray];
            }
        } else {
            [self setDefaultWebArray];
        }
    });
}

- (void)saveWebModelToDisk {
    NSUInteger count = _webModelArray.count;
    WEAK_SELF;
    [_webModelArray enumerateObjectsUsingBlock:^(CLWebModel *model, NSUInteger idx, BOOL *stop){
        CLWebView *webView = model.webView;
        
        if (webView) {
            // Fix dead lock between synchQueue and main queue
            dispatch_main_safe_async(^{
                [webView webViewBackForwardListWithCompletion:^(CLWebViewBackForwardList *backForwardList){
                    if (!backForwardList) {
                        return ;
                    }
                    dispatch_async(weakSelf.synchQueue, ^{
                        NSArray *urls = [weakSelf getBackForwardListURL:backForwardList];
                        NSInteger currentPage = -backForwardList.forwardList.count;
                        model.sessionData = [[CLSessionData alloc] initWithCurrentPage:currentPage urls:urls];
                    });
                }];
            });
        }
        
        if (idx + 1 == count) {
            // Fix dead lock between synchQueue and main queue
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
            dispatch_main_safe_async(^{
                dispatch_async(self.synchQueue, ^{
                    NSMutableData *data = [NSMutableData data];
                    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
                    [archiver encodeObject:self.webModelArray forKey:MY_HISTORY_DATA_KEY];
                    [archiver finishEncoding];
                    [data writeToFile:self.filePath atomically:YES];
                });
            });
#pragma clang diagnostic pop
        }
    }];
}

//save webModel, public API
- (void)saveWebModelData {
    WEAK_SELF;
    dispatch_async(self.synchQueue, ^{
        [weakSelf.webModelArray enumerateObjectsUsingBlock:^(CLWebModel *webModel, NSUInteger idx, BOOL *stop){
            @autoreleasepool {
                if (webModel.webView) {
                    NSString *key = [NSString stringWithFormat:@"%f%@%@",[[NSDate date] timeIntervalSince1970],webModel.url,webModel.title];
                    if ([webModel.url isEqualToString:DEFAULT_CARD_CELL_URL]) {
                        key = DEFAULT_CARD_CELL_URL;
                    }
                    webModel.imageKey = key;
                    if (![weakSelf imageFromDiskCacheForKey:key]) {
                        [weakSelf storeImage:webModel.image forKey:key];
                        NSLog(@"Multi：保存图片> key:[%@]\nvalue:%@",key,webModel.url);
                    }
                }
            }
        }];
        [weakSelf saveWebModelToDisk];
    });
}

- (void)setDefaultWebArray {
    [_webModelArray removeAllObjects];
    [_webModelArray addObject:[self getDefaultWebModel]];
}

- (CLWebModel *)getDefaultWebModel {
    CLWebModel *webModel = [CLWebModel new];
    webModel.title = DEFAULT_CARD_CELL_TITLE;
    webModel.url = DEFAULT_CARD_CELL_URL;
    webModel.imageKey = DEFAULT_CARD_CELL_URL;
    webModel.image = [self imageFromDiskCacheForKey:webModel.imageKey];
    return webModel;
}

- (CLWebModel *)getCurrentWebModel{
    QueueCheck(NO);
    
    __block CLWebModel *webModel = nil;
    dispatch_sync(self.synchQueue, ^{
        webModel = [self.webModelArray lastObject];
    });
    
    return webModel;
}

- (void)setMultiWebViewOperationBlockWith:(MultiWebViewOperationBlock)block{
    WEAK_SELF;
    dispatch_async(self.synchQueue, ^{
        [weakSelf.webModelArray enumerateObjectsUsingBlock:^(CLWebModel *webModel, NSUInteger idx, BOOL *stop){
            [weakSelf startSnapshotForWebModel:webModel];
        }];
        dispatch_main_safe_async(^{
            CLSafeExecuteBlock(block, [weakSelf.webModelArray copy]);
        });
    });
}

- (void)setCurWebViewOperationBlockWith:(CurWebViewOperationBlock)block{
    WEAK_SELF;
    __block CLWebView *webView;
    dispatch_async(self.synchQueue, ^{
        CLWebModel *curModel = [weakSelf.webModelArray lastObject];
        if (!curModel.webView) {
            dispatch_main_safe_async(^{
                webView = [[CLWebView alloc]init];
                webView.scrollView.contentInset = UIEdgeInsetsMake(TOP_TOOL_BAR_HEIGHT, 0, BOTTOM_TOOL_BAR_HEIGHT, 0);
                webView.scrollView.delegate = (id<UIScrollViewDelegate>)CLMainVC;
                dispatch_async(weakSelf.synchQueue, ^{
                    CLWebModel *webModel = weakSelf.webModelArray.lastObject;
                    webModel.webView = webView;
                    webView.webModel = curModel;
                });
                dispatch_main_safe_async(^{
                    CLSafeExecuteBlock(block, curModel, webView);
                });
            });
        } else {
            webView = curModel.webView;
            dispatch_main_safe_async(^{
                CLSafeExecuteBlock(block, curModel, webView);
            });
        }
    });
}

- (void)updateWebModelArray:(NSArray<CLWebModel *> *)webArray{
    [self updateWebModelArray:webArray completion:nil];
}

- (void)updateWebModelArray:(NSArray<CLWebModel *> *)webArray completion:(CLBrowserNoParamsBlock)block{
    NSArray *copyArray = [webArray copy];
    WEAK_SELF;
    dispatch_async(self.synchQueue, ^{
        if (!copyArray.count) {
            [weakSelf setDefaultWebArray];
        } else {
            [weakSelf.webModelArray removeAllObjects];
            [weakSelf.webModelArray addObjectsFromArray:copyArray];
        }
        if (block) {
            dispatch_main_safe_async(block);
        }
    });
}

- (void)switchToLeftOrRight:(BOOL)isLeft completion:(SwitchOperationBlock)block{
    WEAK_SELF;
    dispatch_async(self.synchQueue, ^{
        if (weakSelf.webModelArray.count <= 1) {
            return ;
        }
        
        CLWebModel *prevWebModel = [weakSelf.webModelArray lastObject];
        
        if (isLeft) {
            [weakSelf.webModelArray insertObject:prevWebModel atIndex:0];
            [weakSelf.webModelArray removeLastObject];
        } else {
            [weakSelf.webModelArray addObject:[weakSelf.webModelArray firstObject]];
            [weakSelf.webModelArray removeObjectAtIndex:0];
        }
        
        CLWebModel *curWebModel = [weakSelf.webModelArray lastObject];
        
        if (block) {
            dispatch_main_safe_async(^{
                block(prevWebModel, curWebModel);
            });
        }
    });
}

- (void)switchToLeftWindowWithCompletion:(SwitchOperationBlock)block{
    [self switchToLeftOrRight:YES completion:block];
}

- (void)switchToRightWindowWithCompletion:(SwitchOperationBlock)block{
    [self switchToLeftOrRight:NO completion:block];
}

- (void)addWebModelWithURL:(NSURL *)url completion:(CLBrowserNoParamsBlock)completion{
    if (!url) return;
    WEAK_SELF;
    dispatch_async(self.synchQueue, ^{
        STRONG_SELF;
        NSLog(@"<多窗口> url:%@",url);
        CLWebModel *webModel = [CLWebModel new];
        webModel.url = url.absoluteString;
        [strongSelf.webModelArray addObject:webModel];
        CLSafeExecuteBlock(completion);
    });
}

- (void)stopLoadingCurrentWebView {
    WEAK_SELF;
    dispatch_main_safe_async(^{
        [weakSelf.clContainerView.webView stopLoading];
    });
}

- (NSUInteger)numberOfTabs {
    WEAK_SELF;
    __block NSUInteger num;
    dispatch_sync(self.synchQueue, ^{
        num = weakSelf.webModelArray.count;
    });
    return num;
}

#pragma mark - Disk Image Method

- (void)storeImage:(UIImage *)image forKey:(NSString *)key {
    if (!image || !key) {
        return;
    }
    
    NSData *data = UIImageJPEGRepresentation(image, (CGFloat)1.0f);
    
    if (!data) {
        return;
    }
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    if (![fileManager fileExistsAtPath:self.imagesFolderPath]) {
        [fileManager createDirectoryAtPath:self.imagesFolderPath withIntermediateDirectories:YES attributes:nil error:NULL];
    }
    
    NSString *cachePathForKey = [self defaultCachePathForKey:key];
    
    [fileManager createFileAtPath:cachePathForKey contents:data attributes:nil];
    NSLog(@"Multi：保存路径>%@",cachePathForKey);
}

- (UIImage *)imageFromDiskCacheForKey:(NSString *)key {
    NSString *defaultPath = [self defaultCachePathForKey:key];
    
    UIImage *image = [UIImage imageWithContentsOfFile:defaultPath];
    
    if (image) {
        return  image;
    }
    return nil;
}

- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path {
    NSString *filename = [self cachedFileNameForKey:key];
    return [path stringByAppendingPathComponent:filename];
}

- (NSString *)defaultCachePathForKey:(NSString *)key {
    return [self cachePathForKey:key inPath:self.imagesFolderPath];
}

- (NSString *)cachedFileNameForKey:(NSString *)key {
    const char *str = [key UTF8String];
    if (str == NULL) {
        str = "";
    }
    unsigned char r[CC_MD5_DIGEST_LENGTH];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
    CC_MD5(str, (CC_LONG)strlen(str), r);
#pragma clang diagnostic pop
    NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x.jpg",
                          r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
                          r[11], r[12], r[13], r[14], r[15]];
    
    return filename;
}

#pragma mark - Notification Method

- (void)clearMemory {
    WEAK_SELF;
    dispatch_async(self.synchQueue, ^{
        //remove olddest unused webView
        __block CLWebModel *toDeleteWebModel;
        [weakSelf.webModelArray enumerateObjectsUsingBlock:^(CLWebModel *webModel, NSUInteger idx, BOOL *stop){
            //don't remove newest webView
            if (webModel.webView && idx < weakSelf.webModelArray.count - 1) {
                toDeleteWebModel = webModel;
                *stop = YES;
            }
        }];
        
        toDeleteWebModel.webView = nil;
    });
}

- (void)cleanDisk {
    [self cleanDiskWithCompletionBlock:nil];
}

- (void)backgroundCleanDisk {
    Class UIApplicationClass = NSClassFromString(@"UIApplication");
    if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
        return;
    }
    UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
    __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    
    [self cleanDiskWithCompletionBlock:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
}

- (void)cleanDiskWithCompletionBlock:(CLBrowserNoParamsBlock)completionBlock {
    //save browse data
    [self saveWebModelData];
    WEAK_SELF;
    dispatch_async(self.synchQueue, ^{
        //remove outdated image
        NSMutableSet *urlSet = [NSMutableSet setWithCapacity:self.webModelArray.count];
        [weakSelf.webModelArray enumerateObjectsUsingBlock:^(CLWebModel *webModel, NSUInteger idx, BOOL *stop){
            [urlSet addObject:[[weakSelf defaultCachePathForKey:webModel.imageKey] lastPathComponent]];
        }];
        
        NSURL *diskCacheURL = [NSURL fileURLWithPath:weakSelf.imagesFolderPath isDirectory:YES];
        NSArray *resourceKeys = @[NSURLIsDirectoryKey];
        
        NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtURL:diskCacheURL includingPropertiesForKeys:resourceKeys options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:NULL];
        
        NSMutableArray *urlsToDelete = [NSMutableArray array];
        foreach(fileURL, fileEnumerator) {
            NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
            
            if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
                continue;
            }
            
            if (![urlSet containsObject:[fileURL lastPathComponent]]) {
                [urlsToDelete addObject:fileURL];
            }
        }
        
        foreach(fileURL, urlsToDelete) {
            [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
        }
        
        if (completionBlock) {
            dispatch_main_safe_async(^{
                completionBlock();
            });
        }
    });
}

- (void)noImageModeChanged {
    JSBlock block = ^(CLWebView *webView){
        [CLExtentionsManager evaluateScriptButNotLoadExtentionsWithWebView:webView jsKey:KeyNoImageModeStatus];
    };
    [self enumerateWebViewsToEvaluateJSWithBlock:block];
}

- (void)eyeProtectiveModeChanged {
    JSBlock block = ^(CLWebView *webView) {
        [CLExtentionsManager evaluateScriptButNotLoadExtentionsWithWebView:webView jsKey:KeyEyeProtectiveStatus];
    };
    [self enumerateWebViewsToEvaluateJSWithBlock:block];
}

- (void)enumerateWebViewsToEvaluateJSWithBlock:(JSBlock)block {
    NSCParameterAssert(block);
    NSArray *webArray = [self.webModelArray copy];
    [webArray enumerateObjectsUsingBlock:^(CLWebModel *webModel, NSUInteger idx, BOOL *stop){
        if (webModel.webView) {
            dispatch_main_safe_async(^{
                block(webModel.webView);
            });
        }
    }];
    [[self.webModelArray lastObject].webView reload];
}

#pragma mark - CLWebViewDelegate Method

//当解析完head标签后注入无图模式js,需要注意的是，当启用无图模式时，UIWebView依然会进行图片网络请求,只是设置visible为false
- (void)webView:(CLWebView *)webView gotTitleName:(NSString*)titleName {
    [CLExtentionsManager loadExtentionsIfNeededWhenGotTitleWithWebView:webView];
    [[CLHistorySQLiteManager sharedInstance] insertOrUpdateHistoryWithURL:webView.mainFURL title:titleName];
}

- (void)webView:(CLWebView *)webView didFailLoadWithError:(NSError *)error {
    if (error.code == kCFURLErrorCancelled) {
        return;
    }
    
    NSURL *url = error.userInfo[NSURLErrorFailingURLErrorKey];
    
    if (error.code == kCFURLErrorServerCertificateUntrusted && [url.absoluteString isEqualToString:webView.mainFURL]) {
        [self handleSSLUntrustedWithWebView:webView];
    }
    
    //just trigger error page in case of "http" or "https"
    if ([url.absoluteString isEqualToString:webView.mainFURL] && ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"])) {
        [CLErrorPageHelper showPageWithError:error URL:url inWebView:webView];
        [self saveWebModelData];
    }
}

- (void)webViewForMainFrameDidFinishLoad:(CLWebView *)webView {
    [self startSnapshotForWebModel:webView.webModel];
    [self saveWebModelData];
}

- (void)webViewDidFinishLoad:(CLWebView *)webView {
    [CLExtentionsManager loadExtentionsIfNeededWhenWebViewDidFinishLoad:webView];
}

- (void)webViewForMainFrameDidChangeHtml:(CLWebView *)webView {
    [CLExtentionsManager loadExtentionsIfNeededWhenWebViewDidChangeHtml:webView];
}

// Add basic authentication
- (void)webView:(CLWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    if (challenge.previousFailureCount == 0 && ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] || [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest] || [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM])) {
        NSURLCredential *credential = challenge.proposedCredential;
        if (credential && credential.user.length > 0) {
            [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
        } else if (challenge.protectionSpace.host.length > 0) {
            UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:@"网页认证" message:nil preferredStyle:UIAlertControllerStyleAlert];
            
            UIAlertAction *loginAction = [UIAlertAction actionWithTitle:@"登陆" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                
                NSString *user = actionSheetController.textFields[0].text;
                NSString *pass = actionSheetController.textFields[1].text;
                
                if (user.length > 0 && pass.length > 0) {
                    NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:user password:pass persistence:NSURLCredentialPersistenceForSession];
                    [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
                }
                
            }];
            [actionSheetController addAction:loginAction];
            
            UIAlertAction *cancelAction = [UIAlertAction actionDismiss];
            [actionSheetController addAction:cancelAction];
            
            [actionSheetController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
                textField.placeholder = @"用户名";
            }];
            
            [actionSheetController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
                textField.placeholder = @"密码";
                textField.secureTextEntry = YES;
            }];
            
            if (!CLMainVC.presentedViewController) {
                [CLMainVC presentViewController:actionSheetController animated:YES completion:nil];
            }
        }
    } else {
        [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
}

#pragma mark - SSL Error Handler

- (void)handleSSLUntrustedWithWebView:(CLWebView *)webView {
    UIAlertController *accessDenied = [UIAlertController alertControllerWithTitle:@"您的连接不是私密连接" message:[NSString stringWithFormat:@"攻击者可能会试图从 %@ 窃取您的信息（例如：密码、通讯内容或信用卡信息）。",webView.request.URL.host] preferredStyle:UIAlertControllerStyleActionSheet];
    
    UIAlertAction *dismissAction = [UIAlertAction actionDismiss];
    [accessDenied addAction:dismissAction];
    
    UIAlertAction *continueAction = [UIAlertAction actionWithTitle:@"继续前往（不安全）" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
        CLURLConnectionDelegateProxy *proxy __attribute__((unused)) = [[CLURLConnectionDelegateProxy alloc] initWithURL:webView.request.URL success:^{
            [webView reload];
        } failure:nil];
    }];
    [accessDenied addAction:continueAction];
    
    [CLMainVC presentViewController:accessDenied animated:YES completion:nil];
}

- (void)startSnapshotForWebModel:(CLWebModel *)webModel {
    webModel.isImageProcessed = NO;
    UIImage *image = [webModel.webView snapshotForBrowserWebView];
    if ([webModel.url isEqualToString:DEFAULT_CARD_CELL_URL]) {
        image = [self imageFromDiskCacheForKey:DEFAULT_CARD_CELL_URL];
    } else {
        if (!image) {
            image = [self imageFromDiskCacheForKey:webModel.imageKey];
            if (image) {
                webModel.isImageProcessed = YES;
            }
        }
    }
    if (!image) {
        image = [UIImage imageNamed:DEFAULT_IMAGE];
    }
    webModel.image = image;
    NSLog(@"Multi:截屏key:%@,url:%@,image:%@",webModel.imageKey,webModel.url,image);
}
- (void)nightStatueModeChanged {
    JSBlock block = ^(CLWebView *webView) {
        [CLExtentionsManager evaluateScriptButNotLoadExtentionsWithWebView:webView jsKey:CLKeyNightStatus];
    };
    [self enumerateWebViewsToEvaluateJSWithBlock:block];
}
#pragma mark - Dealloc

- (void)dealloc {
    [Notifier removeObserver:self];
}

@end
